home *** CD-ROM | disk | FTP | other *** search
- /* @(#)pick.c 2.4 (c) copyright 10/18/86 (Dan Heller) */
-
- #include "mush.h"
-
- static int before, after, search_from, search_subj, search_to, xflg, icase;
- static char search_hdr[64];
- static int mdy[3];
- static int pick();
- static void month_day_year();
-
- do_pick(n, argv, list)
- register int n;
- register char **argv, list[];
- {
- char ret_list[MAXMSGS_BITS];
-
- if (n > 1 && !strcmp(argv[1], "-?"))
- return help(0, "pick", cmd_help);
-
- clear_msg_list(ret_list);
- /* if is_pipe, then the messages to search for are already set.
- * if not piped, then reverse the bits for all message numbers.
- * That is, search EACH message. only those matching will be returned.
- */
- if (isoff(glob_flags, IS_PIPE))
- bitput(ret_list, list, msg_cnt, =~); /* macro, turn on all bits */
- /* Use n temporarily as verbosity flag */
- n = (!chk_option("quiet", "pick") &&
- isoff(glob_flags, DO_PIPE));
- if ((n = pick(argv, list, ret_list, n)) == -1)
- return -1;
- if (istool && isoff(glob_flags, DO_PIPE))
- print("%d matches:\n", n);
- for (n = 0; n < msg_cnt; n++)
- if (msg_bit(ret_list, n)) {
- if (isoff(glob_flags, DO_PIPE))
- if (istool)
- print_more("%d ", n+1);
- else
- print("%s\n", compose_hdr(n));
- set_msg_bit(list, n);
- } else
- unset_msg_bit(list, n);
- return 0;
- }
-
- /*
- * search for messages. Return the number of matches. Errors such
- * as internal errors or syntax errors, return -1.
- * "head" and "tail" are specified using +<num> or -<num> as args.
- * Both can be specified and the order is significant.
- * pick +5 -3
- * returns the last three of the first five matches.
- * pick -3 +2
- * returns the first two of the last three matches.
- */
- static int
- pick(argv, list, ret_list, verbose)
- register char **argv, list[], ret_list[];
- {
- register char c;
- int matches = 0;
- char pattern[256];
- short head_first, head_cnt, tail_cnt, search = TRUE;
- int n;
-
- if (!msg_cnt) {
- print("No Messages.\n");
- return -1;
- }
-
- head_first = TRUE;
- head_cnt = tail_cnt = -1;
- icase = before = after = search_from = search_subj = search_to = xflg = 0;
- mdy[0] = mdy[1] = search_hdr[0] = 0;
- while (*argv && *++argv && (**argv == '-' || **argv == '+'))
- if (**argv == '+' || isdigit(argv[0][1])) {
- if (**argv == '+')
- head_cnt = atoi(&argv[0][1]);
- else {
- tail_cnt = atoi(&argv[0][1]);
- if (head_cnt == -1)
- head_first = FALSE;
- }
- if (head_cnt == 0 || tail_cnt == 0) {
- print("pick: invalid head/tail number: %s\n", &argv[0][1]);
- clear_msg_list(ret_list);
- return -1;
- }
- } else if ((c = argv[0][1]) == 'e') {
- if (!*++argv) {
- print("use: -e expression...\n");
- return -1;
- }
- break;
- } else switch (c) {
- /* users specifies a range */
- case 'r': {
- int X = 2;
- /* if not a pipe, then clear all bits cuz we only want
- * to search the message specified here...
- * If it is a pipe, then add to the messages searched for.
- */
- if (isoff(glob_flags, IS_PIPE))
- clear_msg_list(list);
- /* "-r10-15"
- * ^argv[1][2] if NULL, then
- * list detached from "r" e.g. "-r" "5-20"
- */
- if (!argv[0][X])
- argv++, X = 0;
- (*argv) += X;
- n = get_msg_list(argv, list);
- (*argv) -= X;
- if (n == -1)
- return -1;
- argv += (n-1); /* we're going to increment another up top */
- }
- when 'a': {
- if ((n = ago_date(++argv)) == -1)
- return -1;
- argv += n;
- }
- when 'd':
- if (!*++argv) {
- print("Specify a date for -%c\n", c);
- return -1;
- }
- if (!date1(*argv))
- return -1;
- when 's' : case 'f': case 't': case 'h':
- if (search_subj + search_from + search_to + *search_hdr > 1) {
- print("Specify one of `s', `f', `t' or `h' only\n");
- return -1;
- }
- if (c == 's')
- search_subj = 1;
- else if (c == 'f')
- search_from = 1;
- else if (c == 'h')
- if (!*++argv)
- print("Specify header to search for.\n");
- else
- (void) lcase_strcpy(search_hdr, *argv);
- else
- search_to = 1;
- when 'x' : xflg = 1;
- when 'i' : icase = 1;
- otherwise:
- print("pick: unknown flag: %c\n", argv[0][1]);
- clear_msg_list(ret_list);
- return -1;
- }
- if (xflg && head_cnt + tail_cnt >= 0) {
- print("Can't specify -x and head/tail options together.\n");
- return -1;
- }
- pattern[0] = 0;
- (void) argv_to_string(pattern, argv);
- search = (pattern[0] || head_cnt + tail_cnt < 0);
- if (verbose) {
- if (head_cnt + tail_cnt >= 0) {
- print("Finding the ");
- if (head_cnt > 0) {
- if (head_first)
- if (tail_cnt == -1)
- print_more("first %d message%s",
- head_cnt, head_cnt > 1? "s" : "");
- else
- print_more("last %d message%s",
- tail_cnt, tail_cnt > 1? "s" : "");
- else /* there must be a tail_cnt and it comes first */
- print_more("first %d message%s",
- head_cnt, head_cnt > 1? "s" : "");
- } else
- print_more("last %d message%s",
- tail_cnt, tail_cnt > 1? "s" : "");
- if (tail_cnt > 0 && head_cnt > 0)
- if (head_first)
- print_more(" of the first %d", head_cnt);
- else
- print_more(" of the last %d", tail_cnt);
- } else
- print_more("Searching for messages");
- if (!search) {
- if (tail_cnt > 0 && head_cnt > 0)
- print_more(" messages");
- if (ison(glob_flags, IS_PIPE))
- print_more(" from the input list");
- } else if (mdy[1] == 0) {
- print(" that %scontain \"%s\"", (xflg)? "do not ": "",
- (*pattern)? pattern: "<previous expression>");
- if (search_subj)
- print_more(" in subject line");
- else if (search_from)
- print_more(" from author names");
- else if (search_to)
- print_more(" from the To: field");
- else if (search_hdr[0])
- print_more(" from the message header \"%s:\"", search_hdr);
- } else {
- extern char *month_names[]; /* from dates.c */
- print_more(" dated ");
- if (before || after)
- if (xflg)
- print_more("%s ", (!before)? "before": "after");
- else
- print_more("on or %s ", (before)? "before": "after");
- print_more("%s. %d, %d",
- month_names[mdy[0]], mdy[1], mdy[2] + 1900);
- }
- print_more(".\n");
- }
- if (mdy[1] > 0 && icase)
- print("using date: -i flag ignored.\n");
- if (!search) {
- for (n = 0; n < msg_cnt && (!head_first || matches < head_cnt); n++)
- if (msg_bit(list, n))
- ++matches, set_msg_bit(ret_list, n);
- } else
- matches = find_pattern(head_first? head_cnt : msg_cnt,
- pattern, list, ret_list);
- if (xflg && matches >= 0) {
- /* invert items in ret_list that also appear in list */
- bitput(list, ret_list, msg_cnt, ^=);
- /* there should be a faster way to do this count ... */
- for (matches = n = 0; n < msg_cnt; n++)
- if (msg_bit(ret_list, n))
- ++matches;
- }
- Debug("matches = %d\n", matches);
- if (!matches)
- return 0;
-
- /* ok, the list we've got is a list of matched messages. If "tailing"
- * is set, reduce the number of matches to at least tail_cnt.
- */
- if (tail_cnt >= 0)
- for (n = 0; n < msg_cnt && matches > tail_cnt; n++)
- if (msg_bit(ret_list, n)) {
- Debug("tail: dropping %d\n", n+1);
- unset_msg_bit(ret_list, n);
- matches--;
- }
-
- /* if tailing came before heading, we need to do the heading now. */
- if (!head_first && head_cnt >= 0)
- for (n = 0; n < msg_cnt; n++)
- if (msg_bit(ret_list, n))
- if (head_cnt > 0)
- head_cnt--;
- else {
- unset_msg_bit(ret_list, n);
- matches--;
- }
- return matches;
- }
-
- /*
- * find_pattern will search thru all the messages set in the check_list
- * until the list runs out or "cnt" has been exhasted. ret_list contains
- * the list of messages which have matched the pattern.
- * return -1 for internal error or # of pattern matches.
- */
- find_pattern(cnt, p, check_list, ret_list)
- int cnt;
- register char *p;
- char check_list[], ret_list[];
- {
- register int n, val, i; /* val is return value from regex or re_exec */
- int matches = 0;
- long bytes = 0;
- char buf[HDRSIZ];
- static char *err = (char *)-1;
- #ifdef REGCMP
- char *regcmp(), *regex();
- #else /* REGCMP */
- char *re_comp();
- #endif /* REGCMP */
-
- if (p && *p == '\\')
- p++; /* take care of escaping special cases (`-', `\') */
-
- /* specify what we're looking for */
- if (p && *p) {
- if (icase)
- p = lcase_strcpy(buf, p);
- #ifdef REGCMP
- if (err && p)
- xfree(err);
- if (p && !(err = regcmp(p, NULL))) {
- print("regcmp error: %s\n", p);
- clear_msg_list(ret_list);
- return -1;
- }
- #else /* REGCMP */
- if (err = re_comp(p)) {
- print("re_comp error: %s\n", err);
- clear_msg_list(ret_list);
- return -1;
- }
- #endif /* REGCMP */
- } else if (err == (char *)-1 && mdy[1] <= 0) {
- print("No previous regular expression\n");
- clear_msg_list(ret_list); /* doesn't matter really */
- return -1;
- }
- /* start searching: set bytes, and message number: n */
- for (n = 0; cnt && n < msg_cnt; n++)
- if (msg_bit(check_list, n)) {
- if (mdy[1] > 0) {
- int msg_mdy[3];
- if (ison(glob_flags, DATE_RECV))
- p = msg[n].m_date_recv;
- else
- p = msg[n].m_date_sent;
- /* Ick -- fix this mdy thing asap */
- month_day_year(p, &msg_mdy[0], &msg_mdy[1], &msg_mdy[2]);
- Debug("checking %d's date: %d-%d-%d ",
- n+1, msg_mdy[0]+1, msg_mdy[1], msg_mdy[2]);
- /* start at year and wrap around.
- * only when match the day (4), check for == (match)
- */
- for (i = 2; i < 5; i++)
- if (before && msg_mdy[i%3] < mdy[i%3]
- || after && msg_mdy[i%3] > mdy[i%3]
- || i == 4 && (msg_mdy[i%3] == mdy[i%3])) {
- Debug("matched (%s).\n",
- (i == 2)? "year" : (i == 3)? "month" : "day");
- set_msg_bit(ret_list, n);
- cnt--, matches++;
- break;
- } else if (msg_mdy[i%3] != mdy[i%3]) {
- Debug("failed.\n");
- break;
- }
- continue;
- }
- /* we must have the right date -- if we're searching for a
- * string, find it.
- */
- (void) msg_get(n, NULL, 0);
- bytes = 0;
- while (bytes < msg[n].m_size) {
- if (!search_subj && !search_from && !search_to &&
- !*search_hdr && !(p = fgets(buf, sizeof buf, tmpf)))
- break;
- else if (search_subj) {
- if (!(p = header_field(n, "subject")))
- break;
- } else if (search_from) {
- if (!(p = header_field(n, "from"))) {
- /*
- * Check for MSG_SEPARATOR here? Maybe not...
- */
- register char *p2;
- (void) msg_get(n, NULL, 0);
- if (!(p2 = fgets(buf, sizeof buf, tmpf)) ||
- !(p = index(p2, ' ')))
- continue;
- p++;
- if (p2 = any(p, " \t"))
- *p2 = 0;
- }
- } else if (search_to) {
- if (!(p = header_field(n, "to")) &&
- !(p = header_field(n, "apparently-to")))
- break;
- } else if (*search_hdr) {
- if (!(p = header_field(n, search_hdr)))
- break;
- }
- if (icase)
- p = lcase_strcpy(buf, p);
- #ifdef REGCMP
- val = !!regex(err, p, NULL); /* convert value to a boolean */
- #else /* REGCMP */
- val = re_exec(p);
- #endif /* REGCMP */
- if (val == -1) { /* doesn't apply in system V */
- print("Internal error for pattern search.\n");
- clear_msg_list(ret_list); /* it doesn't matter, really */
- return -1;
- }
- if (val) {
- set_msg_bit(ret_list, n);
- cnt--, matches++;
- break;
- }
- if (search_subj || search_from || search_to || *search_hdr)
- break;
- else
- bytes += strlen(p);
- }
- }
- return matches;
- }
-
- #ifdef CURSES
- /*
- * search for a pattern in composed message headers -- also see next function
- * flags == 0 forward search (prompt).
- * flags == -1 continue search (no prompt).
- * flags == 1 backward search (prompt).
- */
- search(flags)
- register int flags;
- {
- register char *p;
- char pattern[128];
- register int this_msg = current_msg, val = 0;
- static char *err = (char *)-1, direction;
- SIGRET (*oldint)(), (*oldquit)();
- #ifdef REGCMP
- char *regcmp();
- #else /* REGCMP */
- char *re_comp();
- #endif /* REGCMP */
-
- if (msg_cnt <= 1) {
- print("Not enough messages to invoke a search.\n");
- return 0;
- }
- pattern[0] = '\0';
- if (flags == -1)
- print("continue %s search...", direction? "forward" : "backward");
- else
- print("%s search: ", flags? "backward" : "forward");
- if (flags > -1)
- if (Getstr(pattern, COLS-18, 0) < 0)
- return 0;
- else
- direction = !flags;
- #ifdef REGCMP
- if (err && *pattern)
- xfree(err);
- else if (err == (char *)-1 && !*pattern) {
- print("No previous regular expression.");
- return 0;
- }
- if (*pattern && !(err = regcmp(pattern, NULL))) {
- print("Error in regcmp in %s", pattern);
- return 0;
- }
- #else /* REGCMP */
- if (err = re_comp(pattern)) {
- print(err);
- return 0;
- }
- #endif /* REGCMP */
- move(LINES-1, 0), refresh();
- on_intr();
-
- do {
- if (direction)
- current_msg = (current_msg+1) % msg_cnt;
- else
- if (--current_msg < 0)
- current_msg = msg_cnt-1;
- p = compose_hdr(current_msg);
- #ifdef REGCMP
- val = !!regex(err, p, NULL); /* convert value to a boolean */
- #else /* REGCMP */
- val = re_exec(p);
- #endif /* REGCMP */
- if (val == -1) /* doesn't apply in system V */
- print("Internal error for pattern search.\n");
- } while (!val && current_msg != this_msg && isoff(glob_flags, WAS_INTR));
-
- if (ison(glob_flags, WAS_INTR)) {
- print("Pattern search interrupted.");
- current_msg = this_msg;
- } else if (val == 0)
- print("Pattern not found.");
-
- off_intr();
- return val;
- }
- #endif /* CURSES */
-
- /*
- * Get just the month, day, and year from a date.
- * This is a temporary measure until the date compares in pick()
- * can be overhauled. It really should be in dates.c, but ...
- */
- static
- void
- month_day_year(date, month, day, year)
- char *date;
- int *month, *day, *year;
- {
- long gmt;
- char unused[4], zone[8];
- struct tm *t;
- extern long getzoff();
-
- (void) sscanf(date, "%ld%3c%s", &gmt, unused, zone);
- gmt += getzoff(zone);
- t = gmtime(&gmt);
- *month = t->tm_mon;
- *day = t->tm_mday;
- *year = t->tm_year;
- }
-
- /*
- * parse a user given date string and set mdy[] array with correct
- * values. Return 0 on failure.
- */
- date1(p)
- register char *p;
- {
- register char *p2;
- long t;
- int i;
- struct tm *today;
-
- if (*p == '-' || *p == '+') {
- before = !(after = *p == '+');
- skipspaces(1);
- }
- if (!isdigit(*p) && *p != '/') {
- print("syntax error on date: \"%s\"\n", p);
- return 0;
- }
- (void) time (&t);
- today = localtime(&t);
- for (i = 0; i < 3; i++)
- if (!p || !*p || *p == '/') {
- switch(i) { /* default to today's date */
- case 0: mdy[0] = today->tm_mon;
- when 1: mdy[1] = today->tm_mday;
- when 2: mdy[2] = today->tm_year;
- }
- if (p && *p)
- p++;
- } else {
- p2 = (*p)? index(p+1, '/') : NULL;
- mdy[i] = atoi(p); /* atoi will stop at the '/' */
- if (i == 0 && (--(mdy[0]) < 0 || mdy[0] > 11)) {
- print("Invalid month: %s\n", p);
- return 0;
- } else if (i == 1 && (mdy[1] < 1 || mdy[1] > 31)) {
- print("Invalid day: %s\n", p);
- return 0;
- }
- if (p = p2) /* set p to p2 and check to see if it's valid */
- p++;
- }
- return 1;
- }
-
- /*
- * Parse arguments specifying days/months/years "ago" (relative to today).
- * Legal syntax: -ago [+-][args]
- * where "args" is defined to be:
- * [0-9]+[ ]*[dD][a-Z]*[ ,]*[0-9]+[mM][a-Z]*[ ,]*[0-9]+[ ]*[yY][a-Z]*
- * 1 or more digits, 0 or more spaces, d or D followed by 0 or more chars,
- * 0 or more whitespaces or commas, repeat for months and years...
- * Examples:
- * 1 day, 2 months, 0 years
- * 2 weeks 1 year
- * 10d, 5m
- * 3w
- * 1d 1Y
- *
- * Return number of args parsed; -1 on error.
- */
- ago_date(argv)
- char **argv;
- {
- #define SECS_PER_DAY (60 * 60 * 24)
- #define SECS_PER_WEEK (SECS_PER_DAY * 7)
- #define SECS_PER_MONTH ((int)(SECS_PER_DAY * 30.5))
- #define SECS_PER_YEAR (SECS_PER_DAY * 365)
- register char *p;
- char buf[256];
- int n = 0, value;
- long t;
- struct tm *today;
-
- (void) argv_to_string(buf, argv);
- p = buf;
- (void) time (&t); /* get current time in seconds and subtract new values */
- if (*p == '-')
- before = TRUE;
- else if (*p == '+')
- after = TRUE;
- skipspaces(before || after);
- while (*p) {
- if (!isdigit(*p)) {
- p -= 2;
- break; /* really a syntax error, but it could be other pick args */
- }
- p = my_atoi(p, &value); /* get 1 or more digits */
- skipspaces(0); /* 0 or more spaces */
- switch (lower(*p)) { /* d, m, or y */
- case 'd' : t -= value * SECS_PER_DAY;
- when 'w' : t -= value * SECS_PER_WEEK;
- when 'm' : t -= value * SECS_PER_MONTH;
- when 'y' : t -= value * SECS_PER_YEAR;
- otherwise: return -1;
- }
- for (p++; Lower(*p) >= 'a' && *p <= 'z'; p++)
- ; /* skip the rest of this token */
- while (*p == ',' || isspace(*p))
- ++p; /* 0 or more whitespaces or commas */
- }
- today = localtime(&t);
- mdy[0] = today->tm_mon;
- mdy[1] = today->tm_mday;
- mdy[2] = today->tm_year;
-
- /* Count the number of args parsed */
- for (n = 0; p > buf && *argv; n++)
- p -= (strlen(*argv++)+1);
- Debug("parsed %d args\n", n);
- return n;
- }
-